﻿using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;

namespace BMServiceGenerator
{
    public class ServiceCreator
    {
        private readonly Arguments _arguments;
        private readonly XPathDocument _document;

        public ServiceCreator(Arguments arguments)
        {
            _document = new XPathDocument(arguments.BamDefinitionPath);
            _arguments = arguments;
        }

        private void CheckPostCompileOutput(string output)
        {
            if (output.Contains("error"))
            {
                throw new ExternalException(string.Format("Found compiler errors. Output: {0}", output));
            }
        }

        public void TransformModel()
        {


            XPathNavigator navigator = _document.CreateNavigator();

            var namespaceManager = new XmlNamespaceManager(navigator.NameTable);
            namespaceManager.AddNamespace("BAM", "http://schemas.microsoft.com/BizTalkServer/2004/10/BAM");

            XPathNodeIterator activities = navigator.Select("BAM:BAMDefinition/BAM:Activity", namespaceManager);

            while (activities.MoveNext())
            {
                string activityName = activities.Current.GetAttribute("Name", "");
                string serviceName = string.Concat(activityName, "Service");

                Transform("BMServiceGenerator.XSLT.ServiceContractGenerator.xslt",
                          string.Format("{0}/I{1}.cs", _arguments.TempFolder, serviceName),
                          activityName,
                          serviceName,
                          _arguments.NamespaceName);

                Transform("BMServiceGenerator.XSLT.ServiceImplementationGenerator.xslt",
                          string.Format("{0}/{1}.svc.cs", _arguments.TempFolder, serviceName),
                          activityName,
                          serviceName,
                          _arguments.NamespaceName);

                Transform("BMServiceGenerator.XSLT.ServiceHostGenerator.xslt",
                          string.Format("{0}/{1}.svc", _arguments.TempFolder, serviceName),
                          activityName,
                          serviceName,
                          _arguments.NamespaceName);

                CheckPostCompileOutput(CompileService(serviceName));

            }

        }

        private void Transform(string xsltPath, string outputPath, string activityName, string serviceName,
                               string namespaceName)
        {
            var arguments = new XsltArgumentList();
            arguments.AddParam("ServiceName", "", serviceName);
            arguments.AddParam("ActivityName", "", activityName);
            arguments.AddParam("NamespaceName", "", namespaceName);

            var reader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream(xsltPath));
            var transformation = new XslCompiledTransform();
            transformation.Load(reader);

            using (var writer = new StreamWriter(outputPath, false))
            {
                transformation.Transform(_document, arguments, writer);
            }
        }

        private string CompileService(string serviceName)
        {
            var compiler = new Process();

            compiler.StartInfo.FileName = RuntimeEnvironment.GetRuntimeDirectory() + "csc.exe";

            compiler.StartInfo.Arguments =
                string.Format("/t:library /out:bin/{0}.dll  *.cs /r:Microsoft.BizTalk.Bam.EventObservation.dll",
                              serviceName);

            compiler.StartInfo.WorkingDirectory = _arguments.TempFolder;

            compiler.StartInfo.UseShellExecute = false;

            compiler.StartInfo.RedirectStandardOutput = true;

            compiler.Start();

            //TODO: måste kolla putput här!!!
            string output = compiler.StandardOutput.ReadToEnd();

            compiler.WaitForExit();

            return output;
        }
    }
}